#include "VolumeOptions.h"
#include "VolumeD3Writer.h"
#include "VolumeJsonWriter.h"
#include <typeinfo>
#include "OCMain/StringConverter.h"

namespace OCPP
{
	namespace Volume
	{
		const String VolumeOptions::Config::NameFillValue = { "FillValue" };
		const String VolumeOptions::Config::NameWriter = { "Writer" };
		const String VolumeOptions::Config::NameOutputPath = { "OutputPath" };
		const String VolumeOptions::Config::NameGZipCompress = { "GZipCompress" };
		const String VolumeOptions::Config::NameMakeDirForFile = { "MakeDirForFile" };
		const String VolumeOptions::Config::NameDimSeperator = { "DimSeperator" };
		const String VolumeOptions::Config::NameEncodeRange = { "EncodeRange" };
		const String VolumeOptions::Config::NameClipRange = { "ClipRange" };
		const String VolumeOptions::Config::NameClipRangeLevel = { "NameClipRangeLevel" };
		const String VolumeOptions::Config::NameReverseY = { "ReverseY" };
		const String VolumeOptions::Config::NameReverseZ = { "ReverseZ" };
		const String VolumeOptions::Config::NameFixValue = { "FixValue" };
		const String VolumeOptions::Config::NameGenerateNormal = { "GenerateNormal" };
		const String VolumeOptions::Config::NameIsoValues = { "NameIsoValues" };

		const String VolumeOptions::Config::ValueD3Writer = { typeid(VolumeD3Writer).name() };
		const String VolumeOptions::Config::ValueJsonWriter = { typeid(VolumeJsonWriter).name() };

		VolumeOptions::VolumeOptions()
		{
			nameDimX = { "c", "x","lon" ,"x1","lon1","longitude" };
			nameDimY = { "r", "y","lat" ,"y1","lat1","latitude" };
			nameDimZ = { "isobaric","height" , "level"
								"isobaric1","height1" , "level1" ,
								"isobaric2","height2" , "level2" };
			nameDimT = { "time","time_bounds" ,
								"time1","time1_bounds" ,
								"time2","time2_bounds"  ,
								"time3","time3_bounds" };
		}

		VolumeOptions::VolumeOptions(const VolumeOptions& other)
		{
			ncWriter = other.ncWriter;
			nameDimX = other.nameDimX;
			nameDimY = other.nameDimY;
			nameDimZ = other.nameDimZ;
			nameDimT = other.nameDimT;
			preferHalfFloat = other.preferHalfFloat;
			absoluteValue = other.absoluteValue;
			reverseY = other.reverseY;
			reverseZ = other.reverseZ;
			clipRect = other.clipRect;
			clipLevelRange = other.clipLevelRange;
			customMinValue = other.customMinValue;
			customMaxValue = other.customMaxValue;
		}

		VolumeOptions::VolumeOptions(VolumeWriter* writer)
		{
			ncWriter.reset(writer);
			nameDimX = { "c", "x","lon" ,"x1","lon1","longitude" };
			nameDimY = { "r", "y","lat" ,"y1","lat1","latitude" };
			nameDimZ = { "isobaric","height" , "level"
								"isobaric1","height1" , "level1" ,
								"isobaric2","height2" , "level2" };
			nameDimT = { "time","time_bounds" ,
								"time1","time1_bounds" ,
								"time2","time2_bounds"  ,
								"time3","time3_bounds" };
		}

		VolumeOptions::VolumeOptions(const StringMap& config, VolumeWriter* writer)
			:VolumeOptions(nullptr)
		{
			auto findConfig = [&config](String key) {
				std::optional<String> result;
				const auto it = config.find(key);
				if (it != config.end())
				{
					result = it->second;
				}
				return result;
				};

			if (writer != NULL)
			{
				ncWriter.reset(writer);
			}
			else
			{
				auto value = findConfig(Config::NameWriter);
				if (value.has_value())
				{
					if (value.value() == Config::ValueD3Writer)
					{
						auto writer = std::make_unique<VolumeD3Writer>();

						{
							auto value1 = findConfig(Config::NameEncodeRange);
							if (value1.has_value())
							{
								writer->setEncodeRange(VolumeEncoder::EncodeRangeFromString(value1.value()));
							}
						}
						ncWriter = std::move(writer);
					}
				}
				if (ncWriter == nullptr)
				{
					ncWriter.reset(new VolumeJsonWriter);
				}
			}

			{
				auto value = findConfig(Config::NameGZipCompress);
				if (value)
				{
					ncWriter->setGZipCompress(StringConverter::parseBool(value.value()));
				}
			}
			{
				auto value = findConfig(Config::NameGenerateNormal);
				if (value)
				{
					ncWriter->setGenerateNormal(StringConverter::parseBool(value.value(), true));
				}
			}
			{
				auto value = findConfig(Config::NameMakeDirForFile);
				if (value.has_value())
				{
					ncWriter->setMakeDirForFile(StringConverter::parseBool(value.value()));
				}
			}
			{
				auto value = findConfig(Config::NameDimSeperator);
				if (value.has_value())
				{
					ncWriter->setDimSeperator(value.value());
				}
			}

			{
				auto value = findConfig(Config::NameOutputPath);
				if (value.has_value())
				{
					ncWriter->setOutputPath(value.value());
				}
			}

			{
				auto value = findConfig(Config::NameFillValue);
				if (value.has_value())
				{
					ncWriter->setFillValue((float)std::atof(value.value().c_str()));
				}
			}

			{
				auto value = findConfig(Config::NameFixValue);
				if (value.has_value())
				{
					ncWriter->setFixValue((float)std::atof(value.value().c_str()));
				}
			}

			{
				auto value = findConfig(Config::NameClipRange);
				if (value.has_value())
				{
					Vector4 vec = StringConverter::parseVector4(value.value());
					if (vec != Vector4::ZERO)
					{
						clipRect = CRectangle(vec[0], vec[1], vec[2], vec[3]);
					}
				}
			}

			{
				auto value = findConfig(Config::NameClipRangeLevel);
				if (value.has_value())
				{
					StringVector sv = StringUtil::split(value.value());
					if (sv.size() == 2)
					{
						clipLevelRange = Vector2i(
							std::stoi(sv[0]), std::stoi(sv[1])
						);
					}
				}
			}

			{
				auto value = findConfig(Config::NameIsoValues);
				if (value.has_value())
				{
					StringVector sv = StringUtil::split(value.value());
					if (sv.size() > 0)
					{
						FloatVector fv;
						for (auto& isoValue : sv)
						{
							fv.push_back(std::stof(isoValue));
						}
						ncWriter->setGenerateIsoValues(fv);
					}
				}
			}

			{
				auto value = findConfig(Config::NameReverseY);
				if (value.has_value())
				{
					reverseY = StringConverter::parseBool(value.value());
				}
			}
			{
				auto value = findConfig(Config::NameReverseZ);
				if (value.has_value())
				{
					reverseZ = StringConverter::parseBool(value.value());
				}
			}
		}

		void VolumeOptions::setWriter(VolumeWriter* writer)
		{
			ncWriter.reset(writer);
		}

		VolumeWriter& VolumeOptions::getWriter() const
		{
			return *ncWriter.get();
		}

		VolumeWriter& VolumeOptions::getWriter()
		{
			return *ncWriter.get();
		}
	}
}


